package com.test.ita;

import org.apache.commons.collections.CollectionUtils;

import javax.swing.*;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by IntelliJ IDEA.
 * User: gpss
 * Date: 7/6/11
 * Time: 1:30 PM
 * To change this template use File | Settings | File Templates.
 */
public class Utils {
    public static final char[] alphabets = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'
            , 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    public static final HashSet<Character> vowels = Utils.getVowels();
    public static final HashSet<Character> consonents = Utils.getConsonents();

    public static final HashMap<String, String> wordImageMap = Utils.readFile();
    public static final HashMap<Character, HashSet<String>> dictionary = Utils.buildDictionary(wordImageMap);
    public static final int MAX_SENTENCE_SIZE = 200;
    public static final String IMAGE_STORE_PATH = "/Users/gpss/ita/Rebusimages/";
    public static final String EXT = ".png";

    /**
     * Reads picture file and constructs HashMap<String, String>
     * key : word
     * val : Associated picture
     *
     * @return HashMap
     */
    //todo externalize file path
    public static HashMap readFile() {
        HashMap<String, String> wordImageMap = new HashMap<String, String>();
        try {
//            FileReader fr = new FileReader("/Users/gpss/ita/Rebusimages/images.txt");
            FileReader fr = new FileReader("images.txt");
            BufferedReader br = new BufferedReader(fr);
            String s;
            int index = 1; //may not be used at real time
            while ((s = br.readLine()) != null) {
                String[] elements = s.split("\\s+");
                if (elements != null && elements.length > 0) {
                    for (int i = 1; i < elements.length; i++) {
                        if (elements[i] != null && elements[i].trim() != null && elements[i].trim() != ""
                                && !" ".equalsIgnoreCase(elements[i])) {
                            wordImageMap.put(elements[i], elements[0]);
                        }
                    }
                }
                index++;
            }

        } catch (FileNotFoundException fe) {
            System.out.println(fe.getMessage());
        } catch (IOException ioe) {
            System.out.println(ioe.getMessage());
        }
        return wordImageMap;
    }

    /**
     * A dictionary is built
     *
     * @param wimap (word image map)
     * @return dictionary
     *         key : char
     *         val : HashSet of words associated with images
     */
    public static HashMap buildDictionary(HashMap<String, String> wimap) {
        long startTime = System.currentTimeMillis();
        HashMap<Character, HashSet<String>> dictionary = new HashMap<Character, HashSet<String>>();
        //init dictionary
        for (char alpha : alphabets) {
            dictionary.put(alpha, new HashSet<String>());
        }
        int si = 1;
        for (Iterator it = wimap.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry entry = (Map.Entry) it.next();
            char[] chars = ((String) entry.getKey()).toCharArray();
            //todo : filter duplicates
            for (char c : chars) {
                dictionary.get((Character) c).add((String) entry.getKey());
            }
        }

        return dictionary;

    }

    /**
     * @param word
     * @return cost (consonents 5, vowels 1
     */
    public static int findWordCost(String word) {

        ArrayList<Character> wlist = new ArrayList<Character>();
        for (char c : word.toCharArray()) {
            wlist.add(c);
        }
        int cost = 0;
        for (Character c : wlist) {
            if (Utils.consonents.contains(c)) {
                cost += 5;
            }
            if (Utils.vowels.contains(c)) {
                cost += 1;
            }
        }
        return cost;
    }

    /**
     * Formula to determine cost
     * word = picture + (word - combination) - (picture - combination)
     *
     * @param word
     * @param combination
     * @param picture
     * @return
     */
    public static RebusNode getRebusNode(String word, String combination, String picture) {

        ArrayList<Character> wlist = new ArrayList<Character>();
        for (char c : word.toCharArray()) {
            wlist.add(c);
        }

        ArrayList<Character> comlist = new ArrayList<Character>();
        for (char c : combination.toCharArray()) {
            comlist.add(c);
        }

        ArrayList<Character> piclist = new ArrayList<Character>();
        for (char c : picture.toCharArray()) {
            piclist.add(c);
        }

//
        //compute difference (word - combination)
        ArrayList<Character> word_comb = (ArrayList<Character>) CollectionUtils.subtract(wlist, comlist);

        StringBuffer w_c_str = new StringBuffer();
        for (Character c : word_comb) {
            w_c_str.append(c);
        }

//        System.out.println("w_c_str : "+ w_c_str);
        ArrayList<Character> pic_comb = (ArrayList<Character>) CollectionUtils.subtract(piclist, comlist);

        StringBuffer p_c_str = new StringBuffer();
        for (Character c : pic_comb) {
            p_c_str.append(c);
        }
//        System.out.println("p_c_str : "+p_c_str);
        int cost = 0;
        int p_c_cost = 0;
        for (Character c : pic_comb) {
            if (Utils.consonents.contains(c)) {
                p_c_cost += 5;
            }
            if (Utils.vowels.contains(c)) {
                p_c_cost += 1;
            }
        }
        int w_c_cost = 0;
        for (Character c : word_comb) {
            if (Utils.consonents.contains(c)) {
                w_c_cost += 5;
            }
            if (Utils.vowels.contains(c)) {
                w_c_cost += 1;
            }
        }
//        System.out.println("Picture : "+picture+" cost: "+(p_c_cost + w_c_cost));
        return new RebusNode(":" + picture, p_c_cost + w_c_cost,
                new RebusNode(w_c_str.toString(), w_c_cost, null, null),
                new RebusNode(p_c_str.toString(), p_c_cost, null, null));

    }

    /**
     * @return set of vowels
     */
    public static final HashSet<Character> getVowels() {
        HashSet<Character> vowels = new HashSet<Character>();
        String vowelString = "aeiou";
        for (char c : vowelString.toCharArray()) {
            vowels.add((Character) c);
        }
        return vowels;
    }

    /**
     * @return set of Consonents
     */
    public static final HashSet<Character> getConsonents() {
        HashSet<Character> consonents = new HashSet<Character>();
        String consonentString = "bcdfghjklmnpqrstvwxyz";
        for (char c : consonentString.toCharArray()) {
            consonents.add((Character) c);
        }
        return consonents;

    }


    public static ArrayList<CombinationBean> findCombinations(String word, int length) {
        ArrayList<CombinationBean> combinations = new ArrayList<CombinationBean>();
        int[] c = new int[length + 3];
        c[0] = -1; // dummy to avoid confusion
        for (int j = 1; j <= length; j++) {
            c[j] = j - 1;
        }
        c[length + 1] = word.length();
        c[length + 2] = 0;
        int j = 1;
        while (j <= length) {
            StringBuffer combBuff = new StringBuffer();
            for (int k = 1; k <= length; k++) {
                combBuff.append(word.toCharArray()[c[k]]);
            }
            combinations.add(new CombinationBean(combBuff.toString()));
            j = 1;
            while (c[j] + 1 == c[j + 1] && j <= length) {
                c[j] = j - 1;
                j++;
            }
            c[j] = c[j] + 1;
        }
        return combinations;
    }

    /**
     * pre order traversal printer
     *
     * @param prefix  - '(', '+', ... etc
     * @param rn      - rebus
     * @param postfix - ')', '-', ... etc
     * @return formatted string - (:pictureWord + AddChars - SubChars)
     */
    public static String printRebus(String prefix, RebusNode rn, String postfix) {
//        System.out.println(rn.getRebus()+ "+" + (rn.getLeft() != null? rn.getLeft().getRebus():"")
//            + "-" + (rn.getRight() != null? rn.getRight().getRebus():""));
        StringBuffer rebusBuff = new StringBuffer();
        if (rn == null || rn.getRebus() == null || rn.getRebus().length() <= 0 || "".equalsIgnoreCase(rn.getRebus())) {
            return "";
        } else {
            System.out.println(prefix + rn.getRebus() + postfix);
            rebusBuff.append(prefix);
            if (!postfix.startsWith(")") && rn.getRebus().startsWith(":")) rebusBuff.append("(");
            rebusBuff.append(rn.getRebus());
            rebusBuff.append(postfix);
        }

        if (rn.getLeft() != null) rebusBuff.append(printRebus("+", rn.getLeft(), ""));
        if (rn.getRight() != null) rebusBuff.append(printRebus("-", rn.getRight(), ")"));

        return rebusBuff.toString();
    }

    /**
     * Level order printer
     *
     * @param rn - rebus node to be printed
     * @return formatted string - (:pictureWord + AddChars - SubChars)
     */
    public static String rebusPrinter(RebusNode rn) {
        if (rn == null) return "";
        StringBuffer rebusBuff = new StringBuffer();
        if (rn.getRebus().startsWith(":")) rebusBuff.append("(");
        rebusBuff.append(rn.getRebus());
        if (rn.getLeft() != null && rn.getLeft().getRebus() != null && !"".equalsIgnoreCase(rn.getLeft().getRebus())) {
            rebusBuff.append("+");
            rebusBuff.append(rebusPrinter(rn.getLeft()));
//            rebusBuff.append(")");
        }

        if (rn.getRight() != null && rn.getRight().getRebus() != null && !"".equalsIgnoreCase(rn.getRight().getRebus())) {
            rebusBuff.append("-");
            rebusBuff.append(rebusPrinter(rn.getRight()));
//            rebusBuff.append(")");

        }
        if (rn.getRebus().startsWith(":")) rebusBuff.append(")");

        return rebusBuff.toString();

    }

    /**
     * Longest Common Subsequence - Dynamic programming table
     *
     * @param word
     * @param picture
     * @return Table containing details of the longest match
     */
    public static LCSCell[][] computeLCS(String word, String picture) {
        char[] wordArr = word.toCharArray();
        char[] picArr = picture.toCharArray();
        LCSCell[][] LCSTable = new LCSCell[wordArr.length + 1][picArr.length + 1];
        //initialize
        for (int i = 0; i <= wordArr.length; i++) {

            LCSTable[i][0] = new LCSCell(0, ' ');
        }

        for (int i = 0; i <= picArr.length; i++) {
            LCSTable[0][i] = new LCSCell(0, ' ');
        }

        for (int w = 1; w <= wordArr.length; w++) {
            for (int p = 1; p <= picArr.length; p++) {

                if (wordArr[w - 1] == picArr[p - 1]) {
//                    System.out.println("wIndex : " + w + " pIndex : " + p);
                    LCSTable[w][p] = new LCSCell(LCSTable[w - 1][p - 1].getVal() + 1, '\\');

                } else if (LCSTable[w - 1][p].getVal() >= LCSTable[w][p - 1].getVal()) {
                    LCSTable[w][p] = new LCSCell(LCSTable[w - 1][p].getVal(), '^');

                } else {
                    LCSTable[w][p] = new LCSCell(LCSTable[w][p - 1].getVal(), '<');

                }
            }
        }

        return LCSTable;

    }

    /**
     * Travels the table to get the longest match
     *
     * @param lcsTable
     * @param word
     * @param wLength
     * @param pLength
     * @return one longest common subsequence is returned (LCS between 'word' & 'picture word'
     */
    public static StringBuffer printLCS(LCSCell[][] lcsTable, String word, int wLength, int pLength) {
        StringBuffer lcs = new StringBuffer();
        if (wLength == 0 || pLength == 0) {
            return lcs;
        }

        if (lcsTable[wLength][pLength].getArrow() == '\\') {
//            printLCS(lcsTable, word, wLength - 1, pLength - 1);
//            System.out.println("wLength : " + wLength);
//            System.out.println(word.charAt(wLength - 1));
            StringBuffer buff = printLCS(lcsTable, word, wLength - 1, pLength - 1);

            lcs.append(word.charAt(wLength - 1) + (buff != null ? buff.toString() : ""));
        } else if (lcsTable[wLength][pLength].getArrow() == '^') {
            StringBuffer buff = printLCS(lcsTable, word, wLength - 1, pLength);
//            printLCS(lcsTable, word, wLength - 1, pLength);
            if (buff != null) {
                lcs.append(buff.toString());
            }
//            lcs.append(printLCS(lcsTable, word, wLength - 1, pLength).toString());
        } else {
            StringBuffer buff = printLCS(lcsTable, word, wLength, pLength - 1);
//            printLCS(lcsTable, word, wLength, pLength - 1);
            if (buff != null) {
                lcs.append(buff.toString());
            }

        }
        return lcs;
    }

    public static HashSet<String> findUniquePictureSet(String word) {
        char[] combChar = word.toCharArray();
        HashSet<String> unionSet = new HashSet<String>();
        long st = System.currentTimeMillis();
        for (Character c : combChar) {
            HashSet<String> charPicSet = Utils.dictionary.get((Character) c);
            unionSet.addAll(charPicSet);
        }
        return unionSet;
    }


    public static String getOptimalPicture(String word) {
        String optimalPicture = "";
        HashSet<String> picSet = findUniquePictureSet(word);

        Distance dis = new Distance();
        for (String pic : picSet) {
            LCSCell[][] lcsCell = Utils.computeLCS(word, pic);
            RebusNode rn = Utils.getRebusNode(word, Utils.printLCS(lcsCell, word, word.length(), pic.length()).reverse().toString(), pic);

            if (dis.LD(word, pic) <= 1 && rn.getCost() <= 1) {
                optimalPicture = pic;
                System.out.println(" Picture selected : " + pic);
            }

        }
        return optimalPicture;
    }

    public static boolean isValid(String word) {
        String expression = "^[a-zA-Z]+";
        boolean isValid = false;
        //Make the comparison case-insensitive.
        Pattern pattern = Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(word);
        if (matcher.matches()) {
            isValid = true;
        }
        return isValid;
    }

    public static ImageIcon getImage(String picture) {
        if (picture == null || picture.length() < 1 || !picture.startsWith(":")) return null;
        System.out.println(Utils.IMAGE_STORE_PATH+
                Utils.wordImageMap.get(picture.substring(1,picture.length())
                ));
        return new ImageIcon(
                Utils.IMAGE_STORE_PATH+
                Utils.wordImageMap.get(picture.substring(1,picture.length())
                ));
    }

    // Tester
//    public static void main(String[] args) {
////        HashMap<String,String> wordImageMap = Utils.readFile();
////        HashMap<Character,HashSet<String>> dictionary = buildDictionary(wordImageMap);
//// //todo we got to add the whole
//// string as a last entry to complete the combination`
////        String word = "solutionget";
//        long st = System.currentTimeMillis();
////        ArrayList<CombinationBean> combs = Utils.findCombinations(word,3);
////        for (CombinationBean cb : combs) {
////            System.out.println("Combs : "+cb.getCombination()+" cost : "+cb.getCost());
////        }
//        long et = System.currentTimeMillis();
//
////        System.out.println("Time taken to create combinations : " + (et - st));
//
////        String word = "cate";
////        String picture = "cat";
////        LCSCell[][] lcsCell = Utils.computeLCS(word, picture);
////        System.out.println(Utils.printLCS(lcsCell, word, word.length(), picture.length()).reverse().toString());
////        Utils.getRebusNode(word,Utils.printLCS(lcsCell, word, word.length(), picture.length()).reverse().toString(),picture);
//
//
//        System.out.println("Optima selection : " + Utils.getOptimalPicture("cate"));
//    }
    public static ArrayList<String> rebusPrintList(RebusNode rn) {
        System.out.println("in util. printlnList");
        if (rn == null) return new ArrayList<String>();
        ArrayList<String> rebusList = new ArrayList<String>();
        if (rn.getRebus().startsWith(":")) rebusList.add("(");
        rebusList.add(rn.getRebus());
        if (rn.getLeft() != null && rn.getLeft().getRebus() != null && !"".equalsIgnoreCase(rn.getLeft().getRebus())) {
            rebusList.add("+");
            rebusList.addAll(rebusPrintList(rn.getLeft()));
//            rebusBuff.append(")");
        }


        if (rn.getRight() != null && rn.getRight().getRebus() != null && !"".equalsIgnoreCase(rn.getRight().getRebus())) {
            rebusList.add("-");
            rebusList.addAll(rebusPrintList(rn.getRight()));
//            rebusBuff.append(")");

        }
        if (rn.getRebus().startsWith(":")) rebusList.add(")");

        return rebusList;

    }
}
